﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace undbx.net
{
    class emlRead
    {

        private enum ENUM {
            EOK = 0,
            EPARSE,
            ENOMEM,
            EBAD2047,
            EINVAL,
            EFULL
        };

        private struct tm
        {
            public int tm_wday;
            public int tm_mday; 
            public int tm_mon;
            public int tm_year;
            public int tm_hour; 
            public int tm_min;
            public int tm_sec;
            public int tm_isdst;
        }

        private static Boolean IsAscii(char c)
        {
            return c >= 0 && c <= 127;
        }

        private static Boolean IsCntrl(char c)
        {
            return c >= 0 && c <= 31 || c == 127;
        }

        private static bool EmlParse822IsLwspChar(byte pChar)
        {
            return Convert.ToChar(pChar) == ' ' || Convert.ToChar(pChar) == '\t';
        }

        private static ENUM EmlParse822SkipLwspChar(byte[] pP, ref int pCurrentIndex)
        {
            if (pCurrentIndex < pP.Length - 1 && EmlParse822IsLwspChar(pP[pCurrentIndex]))
            {
                pCurrentIndex++;
                return ENUM.EOK;
            }

            return ENUM.EPARSE;
        }

        private static ENUM EmlParse822SkipLwsp(byte[] pP, ref int pCurrentIndex)
        {
            bool MySpace = false;

            while(pCurrentIndex != pP.Length-1)
            {
                int MyIndex = pCurrentIndex;

                if (EmlParse822SkipLwspChar(pP, ref pCurrentIndex) == ENUM.EOK)
                {
                    MySpace = true;
                    continue;
                }
                if (EmlParse822SkipNl(pP, ref pCurrentIndex) == ENUM.EOK)
                {
                    if (EmlParse822SkipLwspChar(pP, ref pCurrentIndex) == ENUM.EOK)
                        continue;

                    pCurrentIndex = MyIndex;
                    return ENUM.EPARSE;
                }
                break;
            }

            return MySpace ? ENUM.EOK : ENUM.EPARSE;
        }

        private static ENUM EmlParse822SkipNl(byte[] pP, ref int pCurrentIndex)
        {
            // "0 + ..." został dla czytelności w powiązaniu z kodem w C (tam był czytany indeks 0 dla obecnej wartości wsk.)
            if (pCurrentIndex + 1 < pP.Length - 1 && 
                Convert.ToChar(pP[0 + pCurrentIndex]) == '\r' &&
                Convert.ToChar(pP[1 + pCurrentIndex]) == '\n')
            {
                pCurrentIndex += 2;
                return ENUM.EOK;
            }

            if (pCurrentIndex < pP.Length-1 && Convert.ToChar(pP[0 + pCurrentIndex]) == '\n')
            {
                pCurrentIndex += 1;
                return ENUM.EOK;
            }

            return ENUM.EPARSE;
        }

        private static bool EmlParse822IsSpecial(byte pChar)
        {
            String MyCheck = "()<>@,;:\\\".[]";
            return MyCheck.Contains(Convert.ToChar(pChar));
        }

        private static ENUM EmlParse822Special(byte[] pP, char pC, ref int pCurrIndex)
        {
            EmlParse822SkipLwsp(pP, ref pCurrIndex);

            if (pCurrIndex != pP.Length-1 && Convert.ToChar(pP[pCurrIndex]) == pC)
            {
                pCurrIndex++;
                return ENUM.EOK;
            }

            return ENUM.EPARSE;
        }

        private static ENUM EmlParse822Digits(byte[] pP, ref int pCurrentIndex, int pMin, int pMax, ref int pDigits)
        { 
            int MyIndex = pCurrentIndex;

            pDigits = 0;

            int count = 0;
            while (pCurrentIndex < pP.Length - 1 && Char.IsDigit(Convert.ToChar(pP[pCurrentIndex])))
            {
                pDigits = pDigits * 10 + (Convert.ToChar(pP[pCurrentIndex]) - '0');
                pCurrentIndex++;
                count++;
                if (pMax != 0 && count == pMax)
                    break;
            }
            if (count < pMin)
            {
                pCurrentIndex = MyIndex;
                return ENUM.EPARSE;
            }

            return ENUM.EOK;
        }

        private static ENUM EmlParse822QuotedPair(byte[] pP, ref int pCurrentIndex, List<byte> pQPair)
        {
            if ((pP.Length - 1) - pCurrentIndex < 2)
                return ENUM.EPARSE;

            if (Convert.ToChar(pP[pCurrentIndex]) != '\\')
                return ENUM.EPARSE;

            pQPair.Add(pP[pCurrentIndex]);
            pCurrentIndex += 2;
            return ENUM.EOK;
        }

        private static ENUM EmlParse822Comment(byte[] pP, ref int pCurrentIndex, List<byte> pComment)
        {
            int MyIndex = pCurrentIndex;
            ENUM rc = EmlParse822Special(pP, '(', ref pCurrentIndex);

            if (rc != ENUM.EOK)
                return rc;

            while (pCurrentIndex != pP.Length - 1)
            {
                char c = Convert.ToChar(pP[pCurrentIndex]);
                switch (c)
                {
                    case ')':
                        pCurrentIndex++;
                        return ENUM.EOK;
                    case '(':
                        rc = EmlParse822Comment(pP, ref pCurrentIndex, pComment);
                        break;
                    case '\\':
                        rc = EmlParse822QuotedPair(pP, ref pCurrentIndex, pComment);
                        break;
                    case '\r':
                        pCurrentIndex++; 
                        break;
                    default:
                        if (IsAscii(c))
                        {
                            pComment.Add(Convert.ToByte(c));
                            rc = ENUM.EOK;
                        }
                        pCurrentIndex++;
                        break;
                }
                if (rc != ENUM.EOK)
                    break;
            }

            if (pCurrentIndex == pP.Length - 1)
                rc = ENUM.EPARSE;

            pCurrentIndex = MyIndex;
            return rc;
        }

        private static ENUM EmlParse822SkipComments(byte[] pP, ref int pCurrentIndex)
        {
            ENUM MyStatus;

            List<byte> MyComment = new List<byte>();
            while ((MyStatus = EmlParse822Comment(pP,ref pCurrentIndex, MyComment)) == ENUM.EOK) ;

            return ENUM.EOK;
        }

        private static int EmlParse822FieldName(byte[] pP, ref int Index, out List<byte> pFieldName)
        {
            String MyFieldName = string.Empty;
            List<byte> MyBytes = new List<byte>();
            int Save = Index;

            for(;Index<pP.Length;Index++)
            {
                if (!IsAscii(Convert.ToChar(pP[Index]))) break;
                else if (IsCntrl(Convert.ToChar(pP[Index]))) break;
                else if (Convert.ToChar(pP[Index]) == ' ') break;
                else if (Convert.ToChar(pP[Index]) == ':') break;

                MyBytes.Add(pP[Index]);
            }

            if (!MyBytes.Any())
            {
                Index = Save;
                pFieldName = null;
                return (int)ENUM.EPARSE;
            }

            EmlParse822SkipComments(pP, ref Index);

            if (EmlParse822Special(pP, ':',ref Index) != ENUM.EOK)
            {
                Index = Save;
                pFieldName = null;
                return (int)ENUM.EPARSE;
            }

            pFieldName = MyBytes;
            return (int) ENUM.EOK;
        }

        private static bool EmlParse822IsLWSPChar(char c)
        {
            return (c == ' ' || c == '\t');
        }

        private static int EmlParse822FieldBody(byte[] pP,ref int pIndex, out List<byte> pFieldBody)
        {
            List<byte> result = new List<byte>();

            for (; ; )
            {
                int index = pIndex;
                int i = 0;

                for (; index < pP.Length; index++)
                    if(!EmlParse822IsLWSPChar(Convert.ToChar(pP[index]))) break;

                for (i = index; i < pP.Length; i++)
                    if (Convert.ToChar(pP[i]) == '\r' && i + 1 != pP.Length && Convert.ToChar(pP[i + 1]) == '\n') break;

                for (int j = 0; j < pP.Length; j++)
                    if (j >= index && j < i) result.Add(pP[j]);

                pIndex = i;
                if (pIndex == pP.Length) break;
                pIndex += 2;
                if (pIndex == pP.Length) break;
                if (pP[pIndex] != ' ' && pP[pIndex] != '\t') break;

            }

            pFieldBody = result;
            return (int) ENUM.EOK;
        }

        static int EmlGetword (out byte[] pret, byte[] pstr, ref int pstrIndex, int delim)
        {
          int len = 0;
          byte[] ret;
          
          for(int i = pstrIndex; i<pstr.Length; i++)
          {
              if (pstr[i] == delim)
                  break;
              len++;
          }
          
          pret = null;
          if (len == 0 || len >= pstr.Length - pstrIndex) //jeśli spełniony jest drugi warunek, to znaczy, że nie znaleziono delimitera
            return (int)ENUM.EBAD2047;
          
          ret = new byte[len + 1];
          if (ret.Length == 0)
            return (int)ENUM.ENOMEM;

          for (int i = 0; i < len; i++)
              ret[i] = pstr[pstrIndex + i];
         
          ret[len] = 0;
          pstrIndex = pstrIndex + len + 1;
          pret = ret;
          return 0;
        }

        private static int EmlB64Input(byte pByte)
        {
            List<char> MyChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".ToCharArray().ToList();
            return MyChars.IndexOf(Convert.ToChar(pByte));
        }

        private static int EmlBase64Decode(byte[] pEncodedText,ref byte[] pDecodedText, int pSize, ref int pNBytes)
        {
            int i = 0, tmp = 0, pad = 0, consumed = 0;
            pNBytes = 0;
            byte[] data = new byte[4];
            int EndIndex = 0;
            int DecIndex = 0;

            while (consumed < pSize && pNBytes + 3 < pSize)
            {
                while ((i < 4) && (consumed < pSize))
                {
                    tmp = EmlB64Input(pEncodedText[EndIndex]);
                    EndIndex++;
                    consumed++;
                    if (tmp != -1)
                        data[i++] = Convert.ToByte(tmp);
                    else if (Convert.ToChar(pEncodedText[EndIndex - 1]) == '=')
                    {
                        data[i++] = Convert.ToByte('\0');
                        pad++;
                    }
                }

                if (i == 4)
                {
                    pDecodedText[DecIndex++] = Convert.ToByte((data[0] << 2) | ((data[1] & 0x30) >> 4));
                    pDecodedText[DecIndex++] = Convert.ToByte(((data[1] & 0xf) << 4) | ((data[2] & 0x3c) >> 2));
                    pDecodedText[DecIndex++] = Convert.ToByte(((data[2] & 0x3) << 6) | data[3]);
                    pNBytes += 3 - pad;
                }
                else
                {
                    consumed -= i;
                    return consumed;
                }
                i = 0;
            }
            return consumed;
        }

        private static int EmlQDecode(byte[] pEncodedText,ref byte[] pDecodedText, int pSize, ref int pNBytes)
        {
            byte MyByte;
            int consumed = 0;
            int EndIndex = 0;
            int DecIndex = 0;
            pNBytes = 0;

            while (consumed < pSize && pNBytes < pSize)
            {
                MyByte = pEncodedText[EndIndex++];
                char MyConvChar = Convert.ToChar(MyByte);

                if (MyConvChar == '=')
                {
                    if (consumed + 2 >= pSize)
                        break;
                    else
                    {
                        char[] MyChars = new char[2];
                        long  new_c;

                        MyChars[0] = Convert.ToChar(pEncodedText[EndIndex++]);

                        if (MyChars[0] != '\n')
                        {
                            MyChars[1] = Convert.ToChar(pEncodedText[EndIndex++]);
                            try
                            {
                                new_c = (Int64)Convert.ToUInt64(new string(MyChars), 16);
                            }
                            catch (Exception)
                            {
                                new_c = 0;
                            }
                            pDecodedText[DecIndex++] = Convert.ToByte(new_c);
                            pNBytes++;
                            consumed += 3;
                        }
                        else
                        {
                            consumed += 2;
                        }
                    }
                }
                else if (MyConvChar == '\r')
                {
                    if (consumed + 1 >= pSize)
                        break;
                    else
                    {
                        EndIndex++;
                        pDecodedText[DecIndex++] = Convert.ToByte('\n');
                        pNBytes++;
                        consumed += 2;
                    }
                }
                else if (MyConvChar == '_')
                {
                    pDecodedText[DecIndex++] = Convert.ToByte(' ');
                    pNBytes++;
                    consumed++;
                }
                else
                {
                    pDecodedText[DecIndex++] = MyByte;
                    pNBytes++;
                    consumed++;
                }
            }
            return consumed;
        }

        private static int EmlReallocBuffor(ref byte[] pBuffor, ref int pBuffSize, int pSize)
        {
            int newSize = pBuffSize + pSize;
            byte[] tmp = new byte[newSize];
            Array.Copy(pBuffor, tmp, pBuffor.Length);
            pBuffor = tmp;
            pBuffSize = newSize;
            return 0;
        }

        private static int EmlRFC2047Decode(List<byte> pInput, ref byte[] pToStr)
        {
            if (!pInput.Any()) return (int)ENUM.EINVAL;
            if (!pToStr.Any()) return (int)ENUM.EFULL;

            int status = 0, bufsize = pInput.Count + 1, bufpos = 0, run_count = 0, Index = 0;
            byte[] fromstr = pInput.ToArray();
            byte[] buffer = new byte[bufsize];
            byte[] fromcode = null;
            byte[] encoding_type = null;
            byte[] encoded_text = null;
            byte[] decoded_text = null;
            const int BUFINC = 128;

            while ( Index <= pInput.Count - 1) 
            {
                //dodac sprawdzenie na Index + 1
                if (Convert.ToChar(pInput[Index]) == '=' && Index + 1 <= pInput.Count - 1 && Convert.ToChar(pInput[Index + 1]) == '?')
                {
                    int decoded = 0;
                    int nbytes = 0;
                    int size;
                    int sp =Index + 2;
                 
                    status = EmlGetword(out fromcode, fromstr,ref sp,'?');
                    if (Convert.ToBoolean(status)) break;

                    status = EmlGetword(out encoding_type, fromstr,ref sp, '?');
                    if (Convert.ToBoolean(status)) break;

                    status = EmlGetword(out encoded_text, fromstr,ref sp, '?');
                    if (Convert.ToBoolean(status)) break;

                    if (Convert.ToChar(fromstr[sp]) != '=')
                    {
                        status = (int) ENUM.EBAD2047;
                        break;
                    }
                    
                    size = UnDbxNET.Files.dbxFile.strlen(string.Join("", encoded_text.Select(b => Convert.ToChar(b)))); //.Length;
                    decoded_text = new byte[size];

                    switch (Convert.ToChar(encoding_type[0]))
                    {
                        case 'b':
                        case 'B':
                            decoded = Convert.ToInt32((Convert.ToBoolean(EmlBase64Decode(encoded_text,ref decoded_text, size, ref nbytes)) &&  Convert.ToBoolean(nbytes)));
                            break;

                        case 'q':
                        case 'Q':
                            decoded = Convert.ToInt32((Convert.ToBoolean(EmlQDecode(encoded_text,ref decoded_text, size, ref nbytes)) && Convert.ToBoolean(nbytes)));
                            break;

                        default:
                            status = (int) ENUM.EBAD2047;
                            break;
                    }

                    if (status != 0) break;

                    if (Convert.ToBoolean(decoded))
                    {
                        int MySize = (bufpos + nbytes - bufsize < BUFINC) ? BUFINC : bufpos + nbytes - bufsize;
                        EmlReallocBuffor(ref buffer, ref  bufsize, MySize);

                        string dec_Text = string.Join("", decoded_text.Select(b => Convert.ToChar(b)));
                        

                        for (int i = 0; i < nbytes; i++)
                            buffer[i + bufpos] = decoded_text[i]; 

                        string buf_Text = string.Join("", buffer.Select(b => Convert.ToChar(b)));

                        bufpos += nbytes;
                    }

                    Index = sp + 1;
                    run_count = 1;
                }
                else if (Convert.ToBoolean(run_count))
                {
                    if (Convert.ToChar(pInput[Index]) == ' ' || Convert.ToChar(pInput[Index]) == '\t')
                    {
                        run_count++;
                        Index++;
                        continue;
                    }
                    else
                    {
                        if (Convert.ToBoolean(--run_count))
                        {
                            int MySize = (bufpos + run_count - bufsize < BUFINC) ? BUFINC : bufpos + run_count - bufsize;
                            EmlReallocBuffor(ref buffer, ref  bufsize, MySize);

                            for (int i = 0; i < run_count; i++)
                                buffer[i + bufpos] = fromstr[Index - run_count + i];

                            bufpos += run_count;
                            run_count = 0;
                        }
                        int MySize2 =  (bufpos + 1 - bufsize < BUFINC) ? BUFINC : bufpos + 1 - bufsize;
                        EmlReallocBuffor(ref buffer,ref  bufsize, MySize2);
                        buffer[bufpos++] = pInput[Index++];
                    }
                }
                else
                {
                    int MySize = (bufpos + 1 - bufsize < BUFINC) ? BUFINC : bufpos + 1 - bufsize;
                    EmlReallocBuffor(ref buffer, ref  bufsize, MySize);
                    buffer[bufpos++] = pInput[Index++];
                }
            }

            if (Index != pInput.Count)
            {
                int len = pInput.Count - Index;

                int MySize = (bufpos + len - bufsize < BUFINC) ? BUFINC : bufpos + len - bufsize;
                EmlReallocBuffor(ref buffer, ref  bufsize, MySize);

                for (int i = 0; i < len; i++)
                    buffer[i + bufpos] = pInput[Index + i];

                bufpos += len;
            }

            int MySize3 = (bufpos + 1 - bufsize < BUFINC) ? BUFINC : bufpos + 1 - bufsize;
            EmlReallocBuffor(ref buffer, ref  bufsize, MySize3);
            buffer[bufpos++] = 0;
            pToStr = new byte[bufpos]; 
            Array.Copy(buffer, pToStr, bufpos);


            string str2 = string.Join("", pToStr.Select(b => Convert.ToChar(b)));

            return status;
        }

        public static void EmlParse(byte[] pMessage, ref byte[] pSubject, ref byte[] pFrom, ref byte[] pTo, ref DateTime pTime)
        {
            int Index = 0;
            List<byte> Name = new List<byte>();
            List<byte> Body = new List<byte>();
            Encoding MyEnc = null;

            while (EmlParse822FieldName(pMessage, ref Index, out Name) == (int) ENUM.EOK &&
                EmlParse822FieldBody(pMessage,ref Index, out Body) == (int) ENUM.EOK)
            {
                
                String NameString = string.Join("", Name.Select(b => Convert.ToChar(b)).ToList());
                if (String.Compare(NameString.ToLower(), "subject", true) == 0 && EmlRFC2047Decode(Body, ref pSubject) != (int)ENUM.EOK)
                {
                    pSubject = null;
                }
                else if (String.Compare(NameString.ToLower(), "from", true) == 0 && EmlRFC2047Decode(Body, ref pFrom) != (int)ENUM.EOK)
                {
                    pFrom = null;
                }
                else if (String.Compare(NameString.ToLower(), "to", true) == 0 && EmlRFC2047Decode(Body, ref pTo) != (int)ENUM.EOK)
                {
                    pTo = null;
                }
                else if (String.Compare(NameString.ToLower(), "date", true) == 0)
                {
                    List<byte> MyDate = Body;
                    int MyNewIndex = 0;
                    tm MyTm = new tm();
                    int MyOffset = 0;
                    if (EmlParse822DateTime(MyDate, ref MyNewIndex, ref MyTm, ref MyOffset) == (int)ENUM.EOK)
                    {
                        pTime = EmlMkTime(MyTm, MyOffset);
                    }
                }
                else if (String.Compare(NameString.ToLower(), "content-type", true) == 0)
                {
                    String MyCharSet = string.Join("", Body.Select(b => Convert.ToChar(b))).ToLower();

                    if (MyCharSet.Contains("charset="))
                    {
                        if (MyCharSet.Contains("utf-8"))
                        {
                            MyEnc = Encoding.UTF8;
                        }
                        else if (MyCharSet.Contains("utf-7"))
                        {
                            MyEnc = Encoding.UTF7;
                        }
                        else if (MyCharSet.Contains("utf-32"))
                        {
                            MyEnc = Encoding.UTF32;
                        }
                        else if (MyCharSet.Contains("ascii"))
                        {
                            MyEnc = Encoding.ASCII;
                        }
                        else if (MyCharSet.Contains("unicode"))
                        {
                            MyEnc = Encoding.Unicode;
                        }
                    }
                }
            }

            if (MyEnc != null)
            {
                if (pSubject != null) pSubject = Encoding.Convert(MyEnc, Encoding.Default, pSubject);
                if (pFrom != null) pFrom = Encoding.Convert(MyEnc, Encoding.Default, pFrom);
                if (pTo != null) pTo = Encoding.Convert(MyEnc, Encoding.Default, pTo);
            }
        }

        private static int EmlParse822DateTime(List<byte> pData, ref int pIndex, ref tm pTm, ref int pOffset)
        {
            int rc = 0;
            int save = pIndex;
            int wday = 0;
            int mday = 0;
            int mon = 0;
            int year = 0;
            int hour = 0;
            int min = 0;
            int sec = 0;
            int tzoffset = 0;
            List<byte> MyBytes = new List<byte>();

            if (Convert.ToBoolean(rc = EmlParse822Day(pData, ref pIndex, ref wday)))
            {
                if (rc != (int)ENUM.EPARSE)
                    return rc;
            }
            else
            {
                rc = (int)EmlParse822SkipComments(pData.ToArray(), ref pIndex);

                if (Convert.ToBoolean(rc = (int)EmlParse822Special(pData.ToArray(), ',', ref pIndex)))
                {
                    pIndex = save;
                    return rc;
                }
            }

            if (Convert.ToBoolean(rc = EmlParse822Date(pData, ref pIndex, ref mday, ref mon, ref year)))
            {
                pIndex = save;
                return rc;
            }

            if (Convert.ToBoolean(rc = EmlParse822Time(pData, ref pIndex, ref hour, ref min, ref sec, ref tzoffset, ref MyBytes)))
            {
                pIndex = save;
                return rc;
            }

            pTm = new tm();
            pTm.tm_wday = wday;
            pTm.tm_mday = mday;
            pTm.tm_mon = mon;
            pTm.tm_year = year;
            pTm.tm_hour = hour;
            pTm.tm_min = min;
            pTm.tm_sec = sec;
            pTm.tm_isdst = -1;

            pOffset = tzoffset;

            return (int)ENUM.EOK;
        }

        private static DateTime EmlMkTime(tm pTm, int tzoffset)
        {
            int t = 0;
            int y = pTm.tm_year + 1900;
            int m = pTm.tm_mon + 1;
            int d = pTm.tm_mday;

            if (m < 3)
            {
                m += 12;
                y--;
            }

            t = 86400 * (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 719469);
            t += 3600 * pTm.tm_hour + 60 * pTm.tm_min + pTm.tm_sec;

            DateTime MyDate = new DateTime(1970, 1, 1);
            return MyDate.AddSeconds(t - tzoffset);
        }

        private static int EmlParse822Day(List<byte> pData, ref int pIndex, ref int pDay)
        {
            List<String> MyDays = new List<String>() {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",};

            EmlParse822SkipComments(pData.ToArray(), ref pIndex);

            if (pData.Count - 1 - pIndex < 3) return (int)ENUM.EPARSE; 

            foreach (String MyDay in MyDays)
            {
                String MyDataString = String.Join("", Convert.ToChar(pData[pIndex]), Convert.ToChar(pData[pIndex + 1]), Convert.ToChar(pData[pIndex + 2]));
                if (String.Compare(MyDataString, MyDay, true) == 0)
                {
                    pIndex += 3;
                    pDay = MyDays.IndexOf(MyDay);

                    return (int)ENUM.EOK;
                }
            }

            return (int) ENUM.EPARSE;
        }

        private static int EmlParse822Date(List<byte> pDate, ref int pIndex, ref int pDay, ref int pMon, ref int pYear)
        {
            List<String> MyMonths = new List<String>() { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
            int rc = (int)ENUM.EOK;
            int save = pIndex;
            int yr = 0;
            int yrbeg = 0;
            bool MyIsMonth = false;

            EmlParse822SkipComments(pDate.ToArray(), ref pIndex);

            if (Convert.ToBoolean((rc = (int) EmlParse822Digits(pDate.ToArray(), ref pIndex, 1, 2, ref pDay))))
            {
                pIndex = save;
                return rc;
            }

            EmlParse822SkipComments(pDate.ToArray(), ref pIndex);

            if (pIndex < 3)
                return (int)ENUM.EPARSE;

            foreach (String MyMonth in MyMonths)
            {
                String MyMonthData = String.Join("", Convert.ToChar(pDate[pIndex]), Convert.ToChar(pDate[pIndex + 1]), Convert.ToChar(pDate[pIndex + 2]));
                if (String.Compare(MyMonthData, MyMonth, true) == 0)
                {
                    pIndex += 3;
                    pMon = MyMonths.IndexOf(MyMonth);

                    MyIsMonth = true;
                    break;
                }
            }

            if (!MyIsMonth)
            {
                pIndex = save;
                return (int)ENUM.EPARSE;
            }

            EmlParse822SkipComments(pDate.ToArray(), ref pIndex);

            yrbeg = pIndex;

            if(Convert.ToBoolean((rc = (int) EmlParse822Digits(pDate.ToArray(), ref pIndex, 2, 4, ref yr))))
            {
                pIndex = save;
                return rc;
            }

            switch (pIndex - yrbeg)
            {
                case 2:
                    if (yr >= 0 && yr <= 49)
                    {
                        yr += 2000;
                    }
                    break;
                case 3:
                    yr += 1900;
                    break;
            }

            pYear = yr - 1900;

            return (int) ENUM.EOK;
        }

        private static int EmlParse822Time(List<byte> pData, ref int pIndex, ref int pHour, ref int pMin, ref int pSec, ref int pTz, ref List<byte> pTzName)
        {
            Dictionary<String, int> tzs = new Dictionary<string, int>() { 
                { "UT",   0 * 60 * 60},
                {"UTC",  0 * 60 * 60},
                {"GMT",  0 * 60 * 60},
                {"EST", -5 * 60 * 60},
                {"EDT", -4 * 60 * 60},
                {"CST", -6 * 60 * 60},
                {"CDT", -5 * 60 * 60},
                {"MST", -7 * 60 * 60},
                {"MDT", -6 * 60 * 60},
                {"PST", -8 * 60 * 60},
                {"PDT", -7 * 60 * 60},
                { String.Empty, 0}
            };

            int rc = (int) ENUM.EOK;
            int save = pIndex;
            int z = 0;
            String zone = null;

            EmlParse822SkipComments(pData.ToArray(), ref pIndex);

            if (Convert.ToBoolean((rc = (int) EmlParse822Digits(pData.ToArray(), ref pIndex, 1, 2, ref pHour))))
            {
                pIndex = save;
                return rc;
            }

            if (Convert.ToBoolean((rc = (int)EmlParse822Special(pData.ToArray(), ':', ref pIndex))))
            {
                pIndex = save;
                return rc;
            }

            if (Convert.ToBoolean((rc = (int) EmlParse822Digits(pData.ToArray(),ref pIndex, 1, 2, ref pMin))))
            {
                pIndex = save;
                return rc;
            }

            if (Convert.ToBoolean((rc = (int) EmlParse822Special(pData.ToArray(), ':', ref pIndex))))
                pSec = 0;
            else if (Convert.ToBoolean((rc = (int) EmlParse822Digits(pData.ToArray(), ref pIndex, 1, 2, ref pSec))))
            {
                pIndex = save;
                return rc;
            }

            EmlParse822SkipComments(pData.ToArray(), ref pIndex);

            List<byte> MyListOfBytes = new List<byte>();
            if(Convert.ToBoolean(rc = (int)EmlParse822Atom(pData, ref pIndex, ref MyListOfBytes)))
            {
                if (Convert.ToBoolean(pTz))
                    pTz = 0;

                return (int)ENUM.EOK;
            }

            zone = string.Join("", MyListOfBytes.Select(b => Convert.ToChar(b)));

            for (; z < tzs.Count; z++)
            {
                if (String.Compare(zone, tzs.Keys.ElementAt(z), true) == 0)
                    break;
            }

            if (z != tzs.Count)
            {
                if (pTzName.Any())
                    pTzName = tzs.Keys.ElementAt(z).ToCharArray().Select(c => Convert.ToByte(c)).ToList();

                    pTz = tzs.Values.ElementAt(z);
            }
            else if (zone.Length > 5 || zone.Length < 4)
            {
                if (Convert.ToBoolean(pTz))
                    pTz = 0;
            }
            else
            {
                int hh = 0; ;
                int mm = 0;
                int sign;
                String zp = zone;
                int IndexZp = 0;

                switch (zp[0])
                {
                    case '-':
                        sign = -1;
                        IndexZp++;
                        break;
                    case '+':
                        sign = +1;
                        IndexZp++;
                        break;
                    default:
                        sign = 1;
                        break;
                }

                Regex reNum = new Regex(@"[0-9]+");
                Match m = reNum.Match(zp.Substring(IndexZp));
                if (m.Success && m.Length == 4)
                {
                    hh = (zone[1] - '0') * 10 + (zone[2] - '0');
                    mm = (zone[3] - '0') * 10 + (zone[4] - '0');
                }
                else
                    hh = mm = 0;

                pTz = sign * (hh * 60 * 60 + mm * 60);
            }
            return (int) ENUM.EOK;
        }

        private static int EmlParse822Atom(List<byte> pData, ref int pIndex, ref List<byte> pAtom)
        {
            int rc = (int)ENUM.EPARSE;
            int save = pIndex;

            EmlParse822SkipComments(pData.ToArray(), ref pIndex);
            save = pIndex;

            while (pIndex < pData.Count && (Convert.ToChar(pData[pIndex]) == '.' || EmlParse822IsAtomChar(pData[pIndex])))
            {
                pAtom.Add(pData[pIndex]);
                rc = (int)ENUM.EOK;
                pIndex++;
            }

            return rc;
        }

        private static bool EmlParse822IsAtomChar(byte pByte)
        {
            return IsAscii(Convert.ToChar(pByte)) && Convert.ToBoolean(EmlParse822IsAtomCharEx(pByte));
        }

        private static bool EmlParse822IsAtomCharEx(byte pByte)
        {
            return !EmlParse822IsSpecial(Convert.ToByte(pByte)) && !(Convert.ToChar(pByte) == ' ') && !IsCntrl(Convert.ToChar(pByte));
        }

    }
}
